package proxy.servlet;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class ProxyServlet
 */
@WebServlet("/*")
public class ProxyServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	private String remote = "http://localhost:5000";

	/**
	 * @see HttpServlet#HttpServlet()
	 */
	public ProxyServlet() {
		super();
	}

	/**
	 * @see Servlet#init(ServletConfig)
	 */
	@Override
	public void init(ServletConfig config) throws ServletException {

	}

	/**
	 * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String url = remote + request.getRequestURI();
		if (request.getQueryString() != null)
			url += "?" + request.getQueryString();

		HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();

		connection.setRequestMethod(request.getMethod());

		Enumeration<String> headers = request.getHeaderNames();
		while (headers.hasMoreElements()) {
			String header = headers.nextElement();
			connection.setRequestProperty(header, request.getHeader(header));
		}

		if (request.getContentLength() > 0) {
			connection.setDoOutput(true);
			connection.setFixedLengthStreamingMode(request.getContentLength());
			copy(request.getInputStream(), connection.getOutputStream());
		}

		connection.connect();

		int status = connection.getResponseCode();
		String message = connection.getResponseMessage();
		response.setStatus(status, message);

		for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
			String key = header.getKey();
			String value = StringJoin(header.getValue(), ";");
			if (key == null)
				;
			else if ("Transfer-Encoding".equalsIgnoreCase(key))
				;
			else {
				response.setHeader(key, value);
			}
		}

		copy(connection.getInputStream(), response.getOutputStream());
	}

	private static final int BUF_SIZE = 0x1000; // 4K

	public static long copy(InputStream from, OutputStream to) throws IOException {
		if (from == null)
			throw new IOException("Input stream must not be null.");
		if (to == null)
			throw new IOException("Output stream must not be null.");
		byte[] buf = new byte[BUF_SIZE];
		long total = 0;
		while (true) {
			int r = from.read(buf);
			if (r == -1) {
				break;
			}
			to.write(buf, 0, r);
			total += r;
		}
		return total;
	}

	private static String StringJoin(List<String> list, String separator) {
		StringBuilder sb = new StringBuilder();
		Iterator<String> i = list.iterator();
		boolean hasNext = i.hasNext();
		while (hasNext) {
			String s = i.next();
			hasNext = i.hasNext();
			sb.append(s);
			if (hasNext)
				sb.append(separator);
		}
		return sb.toString();
	}
}
